package com.beiding.my.mql;

import com.beiding.my.bemapping.BeTypeMapper;
import com.beiding.my.bemapping.BeTypeMapperHolder;
import com.beiding.my.bemapping.ExpressionDetails;
import com.beiding.my.bemapping.QuoteDetails;
import com.beiding.my.mql.utils.Block;
import com.beiding.my.mql.utils.KeywordBlock;
import com.beiding.my.mql.utils.MqlUtils;
import com.beiding.my.mql.utils.Pair;
import com.google.common.base.Joiner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

@Component
public class SelectMQLCompiler extends MQLCompiler {

    @Autowired
    private BeTypeMapperHolder boTypeMapperHolder;


    @Override
    public CompilerDetails compile(List<String> ms) {

        StringBuilder builder = new StringBuilder();

        BeTypeMapper beTypeMapper = null;

        boolean flag = true;

        for (String m : ms) {

            CompilerDetails compile = compile(m);
            builder.append(compile.sql).append(" ");
            if (beTypeMapper == null) {
                if (compile.beTypeMapper != null) {
                    beTypeMapper = compile.beTypeMapper;
                }
            } else {
                if (compile.beTypeMapper == null) {
                    flag = false;
                } else {
                    if (beTypeMapper != compile.beTypeMapper) {
                        flag = false;
                    }
                }
            }
        }
        CompilerDetails compilerDetails = new CompilerDetails();
        compilerDetails.type = "select";
        compilerDetails.sql = builder.toString();
        if (flag) {
            compilerDetails.beTypeMapper = beTypeMapper;
        }
        return compilerDetails;

    }

    private CompilerDetails compile(String mql) {

        CompilerDetails compilerDetails = new CompilerDetails();


        Map<String, KeywordBlock> segmentMap = getSegmentMap(mql);

        KeywordBlock selectBlock = segmentMap.remove("select");
        KeywordBlock fromBlock = segmentMap.remove("from");

        if (fromBlock == null || selectBlock == null) {
            throw new MqlException("错误的mql: " + mql);
        }

        String from = fromBlock.getKeywordValue();
        String select = selectBlock.getKeywordValue();


        //可能是多个类型声明
        String[] fromArr = from.split(",");

        if (fromArr.length == 0) {
            throw new MqlException("错误的mql: " + mql);
        }

        //声明的名称和响应的类型mapper
        Map<String, BeTypeMapper> typeMapperMap = new HashMap<>();

        for (String fs : fromArr) {
            fs = fs.trim();
            if (fs.equals("")) {
                throw new MqlException("错误的mql: " + mql);
            }

            String[] tn = fs.split(" +");
            if (tn.length != 2) {
                throw new MqlException("错误的mql: " + mql);
            }

            String t = tn[0].trim();
            String n = tn[1].trim();
            if (t.equals("") || n.equals("")) {
                throw new MqlException("错误的mql: " + mql);
            }

            BeTypeMapper boTypeMapper = boTypeMapperHolder.getBoTypeMapper(t);
            if (boTypeMapper == null) {
                throw new MqlException("不存在的Bo类型: " + t);
            }
            typeMapperMap.put(n, boTypeMapper);
        }

        //处理返回类型, 会有标准格式才能处理成实体
        String[] selectList = select.split(",");

        if (selectList.length == 0) {
            throw new MqlException("错误的mql: " + mql);
        }

        List<ExpressionDetails> expressionDetailsList = new ArrayList<>();

        boolean standard = true;

        BeTypeMapper returnBeTypeMapper = null;

        Map<ExpressionDetails, Block> blockMap = new HashMap<>();

        //判断是否是标准格式
        for (String sl : selectList) {//对于标准格式和非标准格式的情形

            sl = sl.trim();

            Block block = MqlUtils.findStandardReturn(sl, typeMapperMap.keySet());

            if (block == null) {
                standard = false;
                continue;
            }

            String standardReturn = block.getText();

            if (standardReturn == null) {
                continue;
            }

            //返回结果不标准
            if (!sl.equals(standardReturn)) {
                standard = false;
            }

            int i = standardReturn.indexOf('.');

            //找到返回值所属对象
            String name = standardReturn.substring(0, i);
            BeTypeMapper mapper = typeMapperMap.get(name);
            if (returnBeTypeMapper == null) {
                returnBeTypeMapper = mapper;
            } else {
                if (returnBeTypeMapper != mapper) {
                    standard = false;
                }
            }

            if (mapper == null) {
                throw new MqlException("无效声明: " + name);
            }


            //表达式中可能有多个表达式
            ExpressionDetails expressionDetails = mapper.getExpressionDetails(name, standardReturn.substring(i + 1));

            blockMap.put(expressionDetails, block);

            //需要将标准格式进行回填
            expressionDetailsList.add(expressionDetails);

        }

        for (Map.Entry<ExpressionDetails, Block> entry : blockMap.entrySet()) {

            Map<String, String> fieldToIdMap = entry.getKey().getFieldToIdMap();
            Map<String, String> r = new HashMap<>();
            Map<Pair, String> rep = new HashMap<>();
            for (Map.Entry<String, String> stringStringEntry : fieldToIdMap.entrySet()) {
                String s = stringStringEntry.getKey();
                rep.put(entry.getValue(), s);
                String re = MqlUtils.recover(entry.getValue().getSource(), rep);

                r.put(re, stringStringEntry.getValue());
            }
            fieldToIdMap.clear();
            fieldToIdMap.putAll(r);
        }


        Set<String> resultS = new HashSet<>();

        if (standard) {

            for (ExpressionDetails expressionDetails : expressionDetailsList) {

                //如果是标准格式使用as声明为id区段
                Map<String, String> fieldToIdMap = expressionDetails.getFieldToIdMap();
                fieldToIdMap.forEach((k, v) -> {
                    resultS.add(k + " as " + v);
                });
            }
        } else {
            for (ExpressionDetails expressionDetails : expressionDetailsList) {

                Map<String, String> fieldToIdMap = expressionDetails.getFieldToIdMap();
                fieldToIdMap.forEach((k, v) -> {
                    resultS.add(k);
                });

            }
        }


        if (standard && returnBeTypeMapper != null) {
            compilerDetails.beTypeMapper = returnBeTypeMapper;
        }


        //将having,group by,on,order by等所有的标间都进行处理

        //表达式变换
        Map<String, String> keywordExpressionChange = new HashMap<>();


        segmentMap.forEach((keyword, block) -> {

            if (block != null) {

                String text = block.getKeywordValue();

                Set<Block> fields = MqlUtils.findFiled(text, typeMapperMap.keySet());

                //进行字符串展开处理
                Map<Pair, String> rep = new HashMap<>();

                for (Block field : fields) {
                    String sl = field.getText();
                    int i = sl.indexOf('.');
                    String name = sl.substring(0, i);

                    //TODO  一定会存在
                    BeTypeMapper mapper = typeMapperMap.get(name);

                    String e = sl.substring(i + 1);
                    ExpressionDetails expressionDetails = mapper.getExpressionDetails(name, e);


                    //TODO  需要替换回原有数据格式  例如
                    //expressionDetails.realExpression = sl;

                    expressionDetailsList.add(expressionDetails);

                    //TODO 应该仅有一个
                    Map<String, String> fieldToIdMap = expressionDetails.getFieldToIdMap();

                    if (fieldToIdMap.size() != 1) {
                        throw new MqlException("错误的mql(条件错误):" + mql);
                    }


                    //TODO 处理替换逻辑
                    for (String f : fieldToIdMap.keySet()) {
                        rep.put(field, f);
                    }

                }

                text = MqlUtils.recover(text, rep);

                keywordExpressionChange.put(keyword, keyword + " " + text);

            }


        });

        Set<String> fromS = new HashSet<>();

        typeMapperMap.forEach((k, v) -> {
            fromS.add(v.getBeTable() + " " + k);
        });

        Set<String> quoteS = new HashSet<>();


    /*    if (!standard) {
            for (ExpressionDetails details : expressionDetailsList) {
                details.handleFieldToId();
            }
        }*/

        if (expressionDetailsList.size() > 0) {
            for (ExpressionDetails expressionDetails : expressionDetailsList) {

                List<QuoteDetails> quoteDetails = expressionDetails.getQuoteDetails();

                for (QuoteDetails quoteDetail : quoteDetails) {

                    //引用表
                    String quoteTableStatement = quoteDetail.getQuoteTableStatement();
                    String selfStatement = quoteDetail.getSelfStatement();
                    String holderStatement = quoteDetail.getHolderStatement();
                    fromS.add(quoteDetail.getHolderTableName() + " " + holderStatement);
                    fromS.add(quoteDetail.getTargetTableName() + " " + selfStatement);
                    fromS.add(quoteDetail.getQuoteTableName() + " " + quoteTableStatement);

                    //构建引用条件
                    quoteS.add(quoteTableStatement + "." + quoteDetail.getSelfFieldColName() + " = " + selfStatement + "." + quoteDetail.getTargetIdColName());
                    quoteS.add(quoteTableStatement + "." + quoteDetail.getHolderFieldColName() + " = " + holderStatement + "." + quoteDetail.getHolderIdColName());

                }
            }
        }

        if (resultS.size() > 0) {
            String resultString = "select " + Joiner.on(",").join(resultS) + " ";
            keywordExpressionChange.put("select", resultString);
        }


        //一定不为空
        String fromString = "from " + Joiner.on(",").join(fromS) + " ";
        keywordExpressionChange.put("from", fromString);


        //将引用添加到where中去,如果没有where就放在from后面

        if (quoteS.size() > 0) {

            String quoteConditionString = Joiner.on(" and ").join(quoteS);

            String where = keywordExpressionChange.get("where");

            if (where == null) {

                String f = keywordExpressionChange.get("from");
                f = f + " " + quoteConditionString;
                keywordExpressionChange.put("from", f);
            } else {
                where = "where (" + where.substring(6) + ") and (" + quoteConditionString + ")";
                keywordExpressionChange.put("where", where);
            }
        }


        segmentMap.put("select", selectBlock);
        segmentMap.put("from", fromBlock);

        Map<Pair, String> rep = new HashMap<>();

        segmentMap.forEach((k, v) -> {

            String text = v.getText();
            String r = keywordExpressionChange.get(k);
            if (r == null) {
                rep.put(v, text);
            } else {
                rep.put(v, r);
            }

        });

        compilerDetails.sql = MqlUtils.recover(mql, rep);

        return compilerDetails;
    }

    @Override
    public String getCommand() {
        return "select";
    }

    @Override
    public String[] segmentKeywords() {
        return new String[]{"select", "from", "where", "group by", "order by", "on", "having"};
    }

}
